package com.yunkuhui.www.demo.aop;

import com.yunkuhui.www.demo.annotation.MultiRollback;
import com.yunkuhui.www.demo.component.ApplicationContextProviderComponent;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Component
@Aspect
public class MultiTransactionRollback {

	@SuppressWarnings("unused")
	@Pointcut("@annotation(com.yunkuhui.www.demo.annotation.MultiRollback) && execution(* com.yunkuhui.www.demo.service..*.*(..))")
	public void pointCut() {}

	/**
	 * 使用 aop 测试多事务回滚，当前切面抛异常：Transaction synchronization is not active
	 * @param pjp 业务层进程
	 * @param multiRollback 用来获取多个事务
	 * @return 返回业务层的数据到controller
	 * @throws Throwable 业务层抛出的异常
	 */
	@Around("@annotation(multiRollback)")
	public Object around(ProceedingJoinPoint pjp, MultiRollback multiRollback) throws Throwable {
		String[] value = multiRollback.value();
		int length = value.length;
		DataSourceTransactionManager[] transactionManagers = new DataSourceTransactionManager[length];
		TransactionStatus[] statusArr = new TransactionStatus[length];

		for (int i = 0; i < length; i++) {
			transactionManagers[i] = ApplicationContextProviderComponent.getBean(value[i], DataSourceTransactionManager.class);
			DefaultTransactionDefinition def = new DefaultTransactionDefinition();
			def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
			def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
			statusArr[i] = transactionManagers[i].getTransaction(def);
		}
		try {
			Object proceed = pjp.proceed();
			for (int i = 0; i < length; i++) {
				transactionManagers[i].commit(statusArr[i]);
			}
			return proceed;
		} catch (Throwable throwable) {
			for (int i = 0; i < length; i++) {
				transactionManagers[i].rollback(statusArr[i]);
			}
			throw throwable;
		}
	}
}
